use std::{num::ParseIntError, str::FromStr};

use crate::model::date::{Day, Month, Week, Year};
use rocket::form::{self, FromFormField, ValueField};

pub enum ParseDateError {
    Format,
    Number(ParseIntError),
}

impl From<ParseIntError> for ParseDateError {
    fn from(value: ParseIntError) -> ParseDateError {
        ParseDateError::Number(value)
    }
}

impl<'r> From<ParseDateError> for form::Error<'r> {
    fn from(value: ParseDateError) -> Self {
        match value {
            ParseDateError::Format => form::Error::validation("Date format error"),
            ParseDateError::Number(_) => form::Error::validation("Date number out of range"),
        }
    }
}

impl FromStr for Day {
    type Err = ParseDateError;
    fn from_str(param: &str) -> Result<Self, Self::Err> {
        let chunk: Vec<&str> = param.split('-').collect();
        if chunk.len() != 3 {
            return Err(ParseDateError::Format);
        }
        let year = chunk[0].parse()?;
        let month = chunk[1].parse()?;
        let day = chunk[2].parse()?;
        Ok(Day { year, month, day })
    }
}

impl FromStr for Month {
    type Err = ParseDateError;
    fn from_str(param: &str) -> Result<Self, Self::Err> {
        let chunk: Vec<&str> = param.split('-').collect();
        if chunk.len() != 2 {
            return Err(ParseDateError::Format);
        }
        let year = chunk[0].parse()?;
        let month = chunk[1].parse()?;
        Ok(Month { year, month })
    }
}

impl FromStr for Week {
    type Err = ParseDateError;
    fn from_str(param: &str) -> Result<Self, Self::Err> {
        let chunk: Vec<&str> = param.split('-').collect();
        if chunk.len() != 2 {
            return Err(ParseDateError::Format);
        }
        let year = chunk[0].parse()?;
        let week = chunk[1].parse()?;
        Ok(Week { year, week })
    }
}

impl FromStr for Year {
    type Err = ParseDateError;
    fn from_str(param: &str) -> Result<Self, Self::Err> {
        let year = param.parse()?;
        Ok(Year(year))
    }
}

#[rocket::async_trait]
impl<'r> FromFormField<'r> for Day {
    fn from_value(field: ValueField<'r>) -> form::Result<'r, Self> {
        field
            .value
            .parse()
            .map_err(|err: ParseDateError| err.into())
    }
}

#[rocket::async_trait]
impl<'r> FromFormField<'r> for Week {
    fn from_value(field: ValueField<'r>) -> form::Result<'r, Self> {
        field
            .value
            .parse()
            .map_err(|err: ParseDateError| err.into())
    }
}

#[rocket::async_trait]
impl<'r> FromFormField<'r> for Month {
    fn from_value(field: ValueField<'r>) -> form::Result<'r, Self> {
        field
            .value
            .parse()
            .map_err(|err: ParseDateError| err.into())
    }
}

#[rocket::async_trait]
impl<'r> FromFormField<'r> for Year {
    fn from_value(field: ValueField<'r>) -> form::Result<'r, Self> {
        field
            .value
            .parse()
            .map_err(|err: ParseDateError| err.into())
    }
}
